const Events = require("./Events");
const Card = require("./Card");
const CardList = Card.CardList;
const { User } = require("../router/admin");

const { md5 } = require("../unit");

const PLAYER_STATUS = {
  FREE: 0, // "空闲",
  UN_READY: 1, // "未准备",
  READY: 2, // "已准备",
  WAIT_ACTION: 3, // "等待动作",
  CAN_ACTION: 4, // "可动作"
  BEFORE_SUBMIT: 5, // "正在摆牌"
  AFTER_SUBMIT: 6, // "等待其它玩家提交摆牌结果"

  IN_QUEUE: 10, // "匹配队列中",
  BEFORE_QUEUE_TO_ROOM: 11, // "等待确认匹配",
  AFTER_QUEUE_TO_ROOM: 12 // "确认匹配，等待其它玩家确认",
};
class Player extends Events {
  constructor(userinfo) {
    super();
    this.info = userinfo;

    this.status = PLAYER_STATUS.FREE;
    this._is_online = false;
    this.actions = [];
    this.card_list = new CardList();
    const hold_cards = (this.hold_cards = new Map());
    hold_cards.set("flower", new CardList());
    hold_cards.set("peng", new CardList());
    hold_cards.set("chi", new CardList());
    hold_cards.set("an_gang", new CardList());
    hold_cards.set("mi_gang", new CardList());
    this.card_host_list = new CardList();

    this._prop_event_single = new Map();
    this.data = null;
  }
  get id() {
    return (
      this._id ||
      (this._id =
        this.info.id ||
        md5(this.info.openid || this.info.unionid || this.info.nickname))
    );
  }
  get is_online() {
    return this._is_online;
  }
  set is_online(v) {
    const new_v = !!v;
    if (this._is_online !== new_v) {
      this._is_online = new_v;
      this.emitPropChanged("is_online");
    }
  }
  hasCards(cards) {
    const card_list_set = new Set();
    this.card_list.forEach(card_list_set.add.bind(card_list_set));
    return cards.every(card => {
      if (card_list_set.has(card)) {
        card_list_set.delete(card); // 一次性校验
        return true;
      }
    });
  }
  // 顺序排列牌
  sortCards() {
    this.card_list.sort_cards();
  }
  // 抽取指定牌
  removeCards(cards) {
    for (var i = 0; i < cards.length; i++) {
      this.removeCard(cards[i]);
    }
  }
  removeCard(card) {
    var index = this.card_list.indexOf(card);
    if (index != -1) {
      this.card_list.splice(index, 1);
      this.emitPropChanged("card_list");
      return true;
    }
  }
  // 明牌
  addHoldCards(type, cards) {
    const list = this.hold_cards.get(type);
    if (list) {
      this.emitPropChanged("hold_cards", type, cards);
      list.push(...cards);
    }
  }
  addCard(card) {
    this.card_list.push(card);
    this.emitPropChanged("card_list");
  }
  addCards(cards) {
    this.card_list.push(...cards);
    this.emitPropChanged("card_list");
  }
  reset(status = PLAYER_STATUS.FREE, actions = [], is_async = false) {
    this.status = status;
    if (is_async) {
      this.emitPropChangedSync("status");
    } else {
      this.emitPropChanged("status");
    }

    this.actions.length = 0;
    actions.forEach(action => {
      if (typeof action === "string") {
        this.addAction(action);
      } else if (Array.isArray(action)) {
        this.addAction(...action);
      } else if (action && action.name) {
        this.addAction(action.name, action.data);
      }
    });
  }
  joinQueue(QUEUE_SET) {
    // 加入指定 队列
    if (this.getPoints() < 0) {
      throw "水豆不足，无法进行自由匹配";
    }
    this.reset(Player.STATUS.IN_QUEUE);
    QUEUE_SET.add(this);
    const watch_status_to_leave_queue = status => {
      if (
        status !== PLAYER_STATUS.IN_QUEUE &&
        status !== PLAYER_STATUS.BEFORE_QUEUE_TO_ROOM &&
        status !== PLAYER_STATUS.AFTER_QUEUE_TO_ROOM
      ) {
        console.log("status", status, this.status, "移除队列");
        QUEUE_SET.delete(this);
        this.removeListener("statusChanged", watch_status_to_leave_queue);
      }
    };
    this.on("statusChanged", watch_status_to_leave_queue);
  }
  confirmQueueToRoom() {
    // 已经准备好加入房间
    this.reset(PLAYER_STATUS.AFTER_QUEUE_TO_ROOM);
  }
  cancelQueue() {
    //取消队列
    if (
      this.status === PLAYER_STATUS.IN_QUEUE ||
      this.status === PLAYER_STATUS.BEFORE_QUEUE_TO_ROOM
    ) {
      this.reset(PLAYER_STATUS.FREE);
    }
  }
  addAction(action_name, action_data = null) {
    this.actions.push({
      action_name,
      action_data
    });
    this.emitPropChanged("actions");
  }
  canDoAction(action_name) {
    return this.actions.some(action => action === action_name || action);
  }
  getCards() {
    return this.info.cards || 0;
  }
  cutCards(cut_num = 1) {
    return User.cutCards(this.id, cut_num);
  }
  getPoints() {
    return this.info.points || 0;
  }
  cutPoints(cut_num) {
    return User.cutPoints(this.id, cut_num);
  }
  addPoints(add_num) {
    return User.addPoints(this.id, add_num);
  }
  getBotimes() {
    return this.info.botimes || 0;
  }
  addBotimesAndLog(botime_time, log) {
    return User.addBotimesAndLog(this.id, botime_time, log);
  }
  toJSON() {
    const player_info = {
      id: this.id,
      info: {
        username: this.info.nickname,
        headimgurl: this.info.headimgurl,
        cards: this.info.cards,
        points: this.info.points,
        fake: !!this.info.fake
      },
      data: this.data,
      is_online: this.is_online,
      status: this.status,
      actions: this.actions,
      card_list: this.card_list,
      hold_cards: [...this.hold_cards.keys()]
        .map(key => {
          return {
            type: key,
            card_list: this.hold_cards.get(key)
          };
        })
        .filter(key_list => key_list.card_list.length)
    };
    return player_info;
  }
}
Player.STATUS = PLAYER_STATUS;
module.exports = Player;

class PlayerList extends Array {
  next(player) {
    const index = this.indexOf(player);
    return this[(index + 1) % this.length];
  }
  pre(player) {
    const index = this.indexOf(player);
    return this[(index - 1) % this.length];
  }
  other(player) {
    return this.filter(other_player => other_player !== player);
  }
  reset(status, actions) {
    this.forEach(player => player.reset(status, actions));
  }
}
Player.PlayerList = PlayerList;
